
package de.uni.wuppertal.server.ui;

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;
import javax.swing.border.EmptyBorder;

import de.uni.wuppertal.server.ErrorLogEntry;
import de.uni.wuppertal.server.LogEntry;
import de.uni.wuppertal.server.ServerInfo;
import de.uni.wuppertal.server.ServerManager;
import de.uni.wuppertal.server.ui.support.*;
import de.uni.wuppertal.server.util.Resource;

/**
 * @author Martin Schröder
 * @author Patrik Nordsiek
 */
public class MainWindow extends JFrame
{
	private static final long serialVersionUID = 1L;
	
	private static MainWindow instance;
	
	private HelpDialog helpDialog;
	
	private JPanel contentPane;
	
	private JMenuBar mainMenu;
	
	private JMenu fileMenu;
	private JMenu helpMenu;
	
	private JMenuItem exitAction;
	private JMenuItem manualAction;
	private JMenuItem aboutAction;
	
	private JList logList;
	
	private LogListModel logListModel;
	
	private Integer activeServerInstances = 0;
	
	private JLabel serverInstanceCountLabel;
	
	private ServerManager manager;
	private JPanel panel;
	
	private JCheckBox informationCheckbox;
	private JCheckBox warningCheckbox;
	private JCheckBox errorCheckbox;
	private JMenuItem openAction;
	private JMenuItem saveAsAction;
	private JMenuItem newAction;
	private JSeparator separator;
	
	private ServerListModel serverListModel;
	
	private JList serverInstanceList;
	private JCheckBox debugCheckbox;
	
	private JScrollPane logListScrollPane;
	private JPanel panel_2;
	private JButton addServerButton;
	private JButton startAllButton;
	private JButton stopAllButton;
	
	/**
	 * Launch the application.
	 */
	public static void main(String[] args)
	{
		EventQueue.invokeLater(new Runnable()
		{
			public void run()
			{
				try
				{
					UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
					UIManager.put("Button.showMnemonics", true);
					
					MainWindow frame = getInstance();
					frame.setVisible(true);
				}
				catch(Exception e)
				{
					e.printStackTrace();
				}
			}
		});
	}
	
	public static synchronized MainWindow getInstance()
	{
		if(instance == null)
		{
			instance = new MainWindow();
		}
		
		return instance;
	}
	
	/**
	 * Create the frame.
	 */
	private MainWindow()
	{
		manager = new ServerManager();
		
		createUI();
		createMenuActions();
		createActionListeners();
		
		ImageIcon icon = Resource.getIcon("icon/server.png");
		
		setIconImage(icon.getImage());
		
		addWindowListener(new WindowAdapter()
		{
			@Override
			public void windowClosing(WindowEvent event)
			{
				int code = JOptionPane.showConfirmDialog(MainWindow.this, "Wollen Sie wirklich die Anwendung und alle Serverinstanzen beenden?", "Server beenden", JOptionPane.YES_NO_OPTION);
				
				if(code == JOptionPane.YES_OPTION)
				{
					System.exit(0);
				}
			}
		});
	}
	
	private void createUI()
	{
		setTitle("Serververwaltung");
		setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
		setBounds(100, 100, 800, 540);
		
		mainMenu = new JMenuBar();
		setJMenuBar(mainMenu);
		
		fileMenu = new JMenu("Datei");
		fileMenu.setMnemonic('D');
		mainMenu.add(fileMenu);
		
		newAction = new JMenuItem("Neu");
		newAction.setMnemonic('N');
		newAction.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, InputEvent.CTRL_MASK));
		fileMenu.add(newAction);
		
		openAction = new JMenuItem("Öffnen...");
		openAction.setMnemonic('N');
		openAction.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_MASK));
		fileMenu.add(openAction);
		
		saveAsAction = new JMenuItem("Speichern unter...");
		saveAsAction.setMnemonic('S');
		saveAsAction.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_MASK));
		fileMenu.add(saveAsAction);
		
		separator = new JSeparator();
		fileMenu.add(separator);
		
		exitAction = new JMenuItem("Beenden");
		exitAction.setMnemonic('B');
		exitAction.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, InputEvent.CTRL_MASK));
		fileMenu.add(exitAction);
		
		helpMenu = new JMenu("Hilfe");
		helpMenu.setMnemonic('H');
		mainMenu.add(helpMenu);
		
		manualAction = new JMenuItem("Handbuch...");
		manualAction.setMnemonic('H');
		manualAction.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0));
		helpMenu.add(manualAction);
		
		helpMenu.addSeparator();
		
		aboutAction = new JMenuItem("Über...");
		aboutAction.setMnemonic('B');
		aboutAction.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F8, 0));
		helpMenu.add(aboutAction);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		setContentPane(contentPane);
		contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
		
		JPanel centerPanel = new JPanel();
		centerPanel.setBorder(BorderFactory.createTitledBorder("Serverinstanzen"));
		contentPane.add(centerPanel);
		centerPanel.setLayout(new BorderLayout(0, 5));
		
		JPanel toolbarPanel = new JPanel();
		centerPanel.add(toolbarPanel, BorderLayout.SOUTH);
		toolbarPanel.setLayout(new BorderLayout(0, 0));
		
		serverInstanceCountLabel = new JLabel("0");
		serverInstanceCountLabel.setBorder(new EmptyBorder(0, 2, 0, 0));
		toolbarPanel.add(serverInstanceCountLabel, BorderLayout.WEST);
		
		JLabel lblAktiveServerinstanzen = new JLabel(" aktive Serverinstanzen");
		toolbarPanel.add(lblAktiveServerinstanzen, BorderLayout.CENTER);
		
		panel_2 = new JPanel();
		toolbarPanel.add(panel_2, BorderLayout.EAST);
		panel_2.setLayout(new GridLayout(1, 0, 3, 0));
		
		startAllButton = new JButton("Alle starten");
		startAllButton.setMnemonic('S');
		panel_2.add(startAllButton);
		
		stopAllButton = new JButton("Alle anhalten");
		stopAllButton.setMnemonic('A');
		panel_2.add(stopAllButton);
		
		addServerButton = new JButton("Server hinzufügen");
		addServerButton.setMnemonic('S');
		panel_2.add(addServerButton);
		
		JPanel mainPanel = new JPanel();
		centerPanel.add(mainPanel, BorderLayout.CENTER);
		mainPanel.setLayout(new BorderLayout(0, 0));
		
		JScrollPane scrollPane_1 = new JScrollPane();
		scrollPane_1.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
		mainPanel.add(scrollPane_1, BorderLayout.CENTER);
		
		serverListModel = new ServerListModel();
		serverInstanceList = new JList(serverListModel);
		serverInstanceList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		serverInstanceList.setCellRenderer(new ServerListCellRenderer(manager));
		scrollPane_1.setViewportView(serverInstanceList);
		
		JPanel southPanel = new JPanel();
		contentPane.add(southPanel);
		southPanel.setLayout(new BorderLayout(0, 0));
		
		JPanel panel_1 = new JPanel();
		panel_1.setBorder(BorderFactory.createTitledBorder("Server Log"));
		southPanel.add(panel_1);
		panel_1.setLayout(new BorderLayout(0, 0));
		
		panel = new JPanel();
		panel_1.add(panel, BorderLayout.NORTH);
		GridBagLayout gbl_panel = new GridBagLayout();
		gbl_panel.columnWidths = new int[]
		{
			0, 0, 0, 0, 0
		};
		gbl_panel.rowHeights = new int[]
		{
			0, 0
		};
		gbl_panel.columnWeights = new double[]
		{
			0.0, 0.0, 0.0, 1.0, Double.MIN_VALUE
		};
		gbl_panel.rowWeights = new double[]
		{
			0.0, Double.MIN_VALUE
		};
		panel.setLayout(gbl_panel);
		
		debugCheckbox = new JCheckBox("Debug");
		debugCheckbox.setSelected(true);
		GridBagConstraints gbc_debugCheckbox = new GridBagConstraints();
		gbc_debugCheckbox.insets = new Insets(0, 0, 0, 5);
		gbc_debugCheckbox.gridx = 0;
		gbc_debugCheckbox.gridy = 0;
		panel.add(debugCheckbox, gbc_debugCheckbox);
		
		informationCheckbox = new JCheckBox("Informationen");
		informationCheckbox.setSelected(true);
		GridBagConstraints gbc_informationCheckbox = new GridBagConstraints();
		gbc_informationCheckbox.anchor = GridBagConstraints.WEST;
		gbc_informationCheckbox.insets = new Insets(0, 0, 0, 5);
		gbc_informationCheckbox.gridx = 1;
		gbc_informationCheckbox.gridy = 0;
		panel.add(informationCheckbox, gbc_informationCheckbox);
		
		warningCheckbox = new JCheckBox("Warnungen");
		warningCheckbox.setSelected(true);
		GridBagConstraints gbc_warningCheckbox = new GridBagConstraints();
		gbc_warningCheckbox.insets = new Insets(0, 0, 0, 5);
		gbc_warningCheckbox.gridx = 2;
		gbc_warningCheckbox.gridy = 0;
		panel.add(warningCheckbox, gbc_warningCheckbox);
		
		errorCheckbox = new JCheckBox("Fehler");
		errorCheckbox.setSelected(true);
		GridBagConstraints gbc_errorCheckbox = new GridBagConstraints();
		gbc_errorCheckbox.anchor = GridBagConstraints.WEST;
		gbc_errorCheckbox.gridx = 3;
		gbc_errorCheckbox.gridy = 0;
		panel.add(errorCheckbox, gbc_errorCheckbox);
		
		logListScrollPane = new JScrollPane();
		logListScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
		panel_1.add(logListScrollPane, BorderLayout.CENTER);
		
		logListModel = new LogListModel();
		logList = new JList(logListModel);
		logList.setCellRenderer(new LogListCellRenderer());
		
		logListScrollPane.setViewportView(logList);
	}
	
	private void createMenuActions()
	{
		newAction.addActionListener(new ActionListener()
		{
			@Override
			public void actionPerformed(ActionEvent event)
			{
				int code = JOptionPane.showConfirmDialog(MainWindow.this, "Das Anlegen einer neuen Serverkonfiguration beendet alle aktiven Serverinstanzen. Wollen Sie fortfahren?", "Neue Serverkonfiguration", JOptionPane.YES_NO_OPTION);
				
				if(code == JOptionPane.YES_OPTION)
				{
					try
					{
						manager.newConfiguration();
						
						serverListModel.removeAllServers();
					}
					catch(Throwable e)
					{
						log(new ErrorLogEntry(e));
						
						e.printStackTrace();
					}
				}
			}
		});
		
		openAction.addActionListener(new ActionListener()
		{
			@Override
			public void actionPerformed(ActionEvent event)
			{
				JFileChooser chooser = new JFileChooser();
				chooser.setFileFilter(new ConfigurationFileFilter());
				
				int code = chooser.showOpenDialog(MainWindow.this);
				
				if(code == JFileChooser.APPROVE_OPTION)
				{
					try
					{
						manager.loadConfigurationFile(chooser.getSelectedFile());
						
						serverListModel.removeAllServers();
						
						for(ServerInfo info : manager.getServers())
						{
							serverListModel.addServer(info);
						}
					}
					catch(Throwable e)
					{
						log(new ErrorLogEntry(e));
						
						e.printStackTrace();
					}
				}
			}
		});
		
		saveAsAction.addActionListener(new ActionListener()
		{
			@Override
			public void actionPerformed(ActionEvent event)
			{
				JFileChooser chooser = new JFileChooser();
				chooser.setFileFilter(new ConfigurationFileFilter());
				
				int code = chooser.showSaveDialog(MainWindow.this);
				
				if(code == JFileChooser.APPROVE_OPTION)
				{
					try
					{
						manager.saveConfigurationFile(chooser.getSelectedFile());
					}
					catch(Throwable e)
					{
						log(new ErrorLogEntry(e));
						
						e.printStackTrace();
					}
				}
			}
		});
		
		exitAction.addActionListener(new ActionListener()
		{
			@Override
			public void actionPerformed(ActionEvent event)
			{
				int code = JOptionPane.showConfirmDialog(MainWindow.this, "Wollen Sie wirklich die Anwendung und alle Serverinstanzen beenden?", "Server beenden", JOptionPane.YES_NO_OPTION);
				
				if(code == JOptionPane.YES_OPTION)
				{
					System.exit(0);
				}
			}
		});
		
		manualAction.addActionListener(new ActionListener()
		{
			@Override
			public void actionPerformed(ActionEvent event)
			{
				EventQueue.invokeLater(new Runnable()
				{
					@Override
					public void run()
					{
						getHelpDialog().setVisible(true);
					}
				});
			}
		});
		
		aboutAction.addActionListener(new ActionListener()
		{
			@Override
			public void actionPerformed(ActionEvent event)
			{
				AboutDialog dialog = new AboutDialog(MainWindow.this);
				dialog.setVisible(true);
			}
		});
	}
	
	private void createActionListeners()
	{
		startAllButton.addActionListener(new ActionListener()
		{
			@Override
			public void actionPerformed(ActionEvent event)
			{
				for(ServerInfo info : manager.getServers())
				{
					try
					{
						manager.startServer(info);
					}
					catch(Throwable e)
					{
						log(new ErrorLogEntry(e));
					}
				}
				
				EventQueue.invokeLater(new Runnable()
				{
					@Override
					public void run()
					{
						serverInstanceList.repaint();
					}
				});
			}
		});
		
		stopAllButton.addActionListener(new ActionListener()
		{
			@Override
			public void actionPerformed(ActionEvent event)
			{
				try
				{
					manager.shutdownServers();
				}
				catch(Throwable e)
				{
					log(new ErrorLogEntry(e));
				}
				
				EventQueue.invokeLater(new Runnable()
				{
					@Override
					public void run()
					{
						serverInstanceList.repaint();
					}
				});
			}
		});
		
		addServerButton.addActionListener(new ActionListener()
		{
			@Override
			public void actionPerformed(ActionEvent event)
			{
				AddServerDialog dialog = new AddServerDialog(MainWindow.this);
				dialog.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
				dialog.setVisible(true);
			}
		});
		
		serverInstanceList.addMouseListener(new MouseAdapter()
		{
			private final ImageIcon runIcon = Resource.getIcon("icon/server-connect.png");
			private final ImageIcon removeIcon = Resource.getIcon("icon/server-delete.png");
			
			@Override
			public void mousePressed(MouseEvent event)
			{
				if(SwingUtilities.isRightMouseButton(event) && !serverInstanceList.isSelectionEmpty())
				{
					if(serverInstanceList.getSelectedIndex() == serverInstanceList.locationToIndex(event.getPoint()))
					{
						JPopupMenu menu = new JPopupMenu();
						
						final ServerInfo info = (ServerInfo)serverInstanceList.getSelectedValue();
						final boolean running = manager.isRunning(info);
						
						JMenuItem runAction = new JMenuItem(running ? "Server anhalten" : "Server starten");
						runAction.setIcon(runIcon);
						
						runAction.addActionListener(new ActionListener()
						{
							@Override
							public void actionPerformed(ActionEvent evt)
							{
								try
								{
									if(running)
									{
										manager.shutdownServer(info);
									}
									else
									{
										manager.startServer(info);
									}
								}
								catch(Throwable t)
								{
									log(new ErrorLogEntry(t));
								}
							}
						});
						
						menu.add(runAction);
						
						JMenuItem removeAction = new JMenuItem("Server entfernen");
						removeAction.setIcon(removeIcon);
						
						removeAction.addActionListener(new ActionListener()
						{
							@Override
							public void actionPerformed(ActionEvent evt)
							{
								try
								{
									int code = JOptionPane.showConfirmDialog(MainWindow.this, "Wollen Sie diesen Server löschen?\n" + info.getName(), "Serverinstanz entfernen", JOptionPane.OK_CANCEL_OPTION);
									
									if(code == JOptionPane.OK_OPTION)
									{
										manager.removeServer(info);
										
										serverListModel.removeServer(info);
									}
								}
								catch(Throwable e)
								{
									log(new ErrorLogEntry(e));
								}
							}
						});
						
						menu.add(removeAction);
						
						menu.show(serverInstanceList, event.getX(), event.getY());
					}
				}
			}
		});
		
		debugCheckbox.addActionListener(new ActionListener()
		{
			@Override
			public void actionPerformed(ActionEvent event)
			{
				if(debugCheckbox.isSelected())
				{
					logListModel.addFilter(LogListModel.SHOW_DEBUG);
				}
				else
				{
					logListModel.removeFilter(LogListModel.SHOW_DEBUG);
				}
			}
		});
		
		informationCheckbox.addActionListener(new ActionListener()
		{
			@Override
			public void actionPerformed(ActionEvent event)
			{
				if(informationCheckbox.isSelected())
				{
					logListModel.addFilter(LogListModel.SHOW_INFORMATION);
				}
				else
				{
					logListModel.removeFilter(LogListModel.SHOW_INFORMATION);
				}
			}
		});
		
		warningCheckbox.addActionListener(new ActionListener()
		{
			@Override
			public void actionPerformed(ActionEvent event)
			{
				if(warningCheckbox.isSelected())
				{
					logListModel.addFilter(LogListModel.SHOW_WARNING);
				}
				else
				{
					logListModel.removeFilter(LogListModel.SHOW_WARNING);
				}
			}
		});
		
		errorCheckbox.addActionListener(new ActionListener()
		{
			@Override
			public void actionPerformed(ActionEvent event)
			{
				if(errorCheckbox.isSelected())
				{
					logListModel.addFilter(LogListModel.SHOW_ERROR);
				}
				else
				{
					logListModel.removeFilter(LogListModel.SHOW_ERROR);
				}
			}
		});
	}
	
	private synchronized HelpDialog getHelpDialog()
	{
		if(helpDialog == null)
		{
			helpDialog = new HelpDialog(this);
		}
		
		return helpDialog;
	}
	
	public synchronized void log(final LogEntry entry)
	{
		logListModel.prepend(entry);
		logList.repaint();
	}
	
	public void incrementActiveServerInstance()
	{
		synchronized(activeServerInstances)
		{
			activeServerInstances++;
			
			serverInstanceCountLabel.setText(String.valueOf(activeServerInstances));
		}
	}
	
	public void decrementActiveServerInstance()
	{
		synchronized(activeServerInstances)
		{
			activeServerInstances--;
			
			serverInstanceCountLabel.setText(String.valueOf(activeServerInstances));
		}
	}
	
	public ServerManager getManager()
	{
		return manager;
	}
	
	public synchronized void addServer(ServerInfo server) throws Throwable
	{
		manager.addServer(server);
		serverListModel.addServer(server);
	}
}
